#include <mips/regdef.h>
#include <sys/syscall.h>

#define RES_OK 0
#define ERROR_ESCRITURA 1
#define ERROR_LECTURA -2
#define TAM_BUFFER 6

	.text
	.align 2
	.global push_bit_to_buffer
	.ent	push_bit_to_buffer
push_bit_to_buffer:
	.frame	 $fp, 32,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 32
	.cprestore 24
	sw		 $fp, 20(sp)		#Guardo el $fp en el frame
	sw		 ra, 16(sp)			#Guardo el ra en el frame
	move	 $fp,  sp			#Seteo el $fp

	sw		a0, 32($fp)			#a0: buffer
	sw		a1, 36($fp)			#a1: bit
	sw		a2, 40($fp)			#a2: file desc

	lbu		t1, 0(a0)			#Cargo en t1 el buffer
	sll		t1, t1, 1			#buffer = buffer << 1

	beq		a1, zero, bit_zero	#Si el caracter a escribir es 0 no agrego nada
	addiu	t1, t1, 1			#Pongo un 1 en la primer posicion (dsp del corrimiento)

bit_zero:
	sb		t1, 0(a0)			#Actualizo el buffer en memoria

	lbu		t1, 1(a0)			#Cargo en t1 el indice y lo incremento
	addiu	t1, t1, 1
	sb		t1, 1(a0)			#Actualizo el indice incrementado

	li		v0, RES_OK				#Seteo el valor de retorno
	li		t0, TAM_BUFFER		#Cargo en t0 el tamanio del buffer para comparar con el indice
	bne		t0, t1, return_push_bit

	lbu		a0, 0(a0)			#a0: arg1 de get_encoding, el valor a codificar.
	jal		get_encoding

	sw		v0, 8($fp)			#Guardo el valor de la codificacion.

	li		 v0, SYS_write		# ver dentro de <sys/syscall.h>.
	lw		 a0, 40($fp)		# a0: file descriptor recibido como argumento
	la	 	 a1, 8($fp)       	# a1: Almaceno en a1 el caracter a escribir
	li		 a2, 1	      		# a2: bytes a escribir: 1b.

	syscall

	bne 	 zero, a3, error_write	#Si a3 != 0, error de escritura
	li		 t0, 1
	bne 	 v0, t0, error_write	#Si v0 != 1, error de escritura

	lw		a0, 32($fp)				#Cargo el puntero en a0
	sw	 	zero, 0(a0)				#Reinicio el buffer
	li		v0, RES_OK				#Seteo el valor de retorno
	b		return_push_bit
error_write:
	li		 v0, ERROR_ESCRITURA	#Seteo v0 en el codigo de retorno 1 (error de escritura)
return_push_bit:
	lw		$fp, 20(sp)		#Recupero $fp
	lw		ra, 16(sp)		#Recupero ra
	addiu	sp, sp, 32 		#Restauro el stack
	j	ra
	.end	push_bit_to_buffer

	.global get_encoding
	.ent get_encoding
get_encoding:
	.frame	 $fp, 16,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 16
	.cprestore 12
	sw		 $fp, 4(sp)		#Guardo el $fp en el frame
	move	 $fp,  sp			#Seteo el $fp
	ori		 t0,  zero, 25			#Seteo t0 en 25
	bgtu	 a0,  t0, NO_CAP_LETTER	#Si es mayor a 25, no es Mayuscula
	addiu	 v0,  a0, 65			#devuelvo arg + 65
	b		return_get_encoding
NO_CAP_LETTER:
	ori		 t0,  zero, 51			#Seteo t0 en 51
	bgtu	 a0,  t0, ES_NUM		#Si es mayor a 51, es Numero
	addiu	 v0,  a0, 71			#devuelvo arg + 71
	b		return_get_encoding
ES_NUM:
	ori		 t0,  zero, 61			#Seteo t0 en 61
	bgtu	 a0,  t0, ES_ESPECIAL	#Si es mayor a 61, es caracter especial
	subu	 v0,  a0, 4				#devuelvo arg -= 4
	b		return_get_encoding
ES_ESPECIAL:
	ori		 t0,  zero, 62			#Seteo t0 en 62
	beq		 a0,  t0, ES_43			#Si es igual a 62, es caracter 43.
	ori		 v0,  zero, 47			#devuelvo 47
	b		return_get_encoding
ES_43:
	ori		 v0,  zero, 43			#devuelvo 43

return_get_encoding:
	lw		 $fp, 4(sp)		#Recupero $fp
	addiu	sp , sp,16 		# Restauro el stack
	j	ra
	.end get_encoding


	.global read_from_input
	.ent	read_from_input
read_from_input:
	.frame	 $fp, 32,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 32
	.cprestore 28
	sw		$fp, 24(sp)		#Guardo el $fp en el frame
	sw		ra, 20(sp)		#Guardo el ra en el frame
	sw		s0,	16(sp)		#Guardo s0 en el frame
	move	$fp,  sp		#Seteo el $fp
	sw		a0, 32($fp)			#Guardo el puntero a encode_block
	sw		a1, 36($fp)			#Guardo el file desc

	sw	 	zero, 0( a0)		#Pongo en 0 el encode_block

	sw		zero, 8($fp)		#Inicializo variable local contador en 0
	li		s0, 0				#Inicializo el indice en 0
DO:
	li		v0, SYS_read		# ver dentro de <sys/syscall.h>.
	lw		a0, 36($fp)			# a0: file descriptor recibido por parametro.
	la	 	a1, 12($fp)     	# a1: Posicion donde almaceno el byte leido.
	li		a2, 1	      		# a2: bytes a escribir: 1b.

	syscall

	li		t0, 1
	bnez	a3, error			#Si a3 != 0 error de lectura
	bne		v0, t0, end_while	#Si v0 != 1	error de lectura

	lbu		t0, 12($fp)			#Cargo en t0 el byte leido
	lw		t2, 32($fp)			#Cargo el puntero al encode_block
	add		t2, t2, s0			#Le sumo el offset
	sb		t0, 0(t2)			#Guardo el byte leido (t0)

	lw		t0, 8($fp)			#Cargo la variable contador
	addiu	t0, t0, 1			#Le sumo el resultado del syscall
	sw		t0, 8($fp)			#Guardo el contador
	addiu	s0, 1

	li 		t0, 3
	blt		s0, t0, DO		#Si el indice < 3 salto

end_while:
	lw		v0, 8($fp)		#Seteo el valor de retorno
	b		return_read
error:
	li		v0, ERROR_LECTURA
return_read:
	lw		 $fp, 24(sp)	#Recupero $fp
	lw		 ra, 20(sp)		#Recupero ra
	lw		 s0, 16(sp)		#Recupero s0
	addiu	 sp , sp,32 	# Restauro el stack
	j	ra
	.end	read_from_input



	.global write_buffer
	.ent	write_buffer
write_buffer:
	.frame	 $fp, 40,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 40
	.cprestore 32
	sw		 $fp, 28(sp)		#Guardo el $fp en el frame
	sw		 ra, 24(sp)			#Guardo el ra en el frame
	move	 $fp,  sp			#Seteo el $fp

	sw		a0, 40($fp)			#a0: buffer
	sw		a1, 44($fp)			#a1: byte
	sw		a2, 48($fp)			#a2: finalBit
	sw		a3, 52($fp)			#a3: file descriptor

	sw		s0, 16($fp)			#s0: i del for(i;;)
	sw		s1, 20($fp)			#s1: index

	move	s0, a2				# i = finalBit
	li		s1, 1
	sll		s1, s1, 31			#index = 1 << 31

COND:
	beq		zero, s0, END 			#si i == 0 termino

	lw		a0, 40($fp)			#a0: buffer

	lw		t0, 44($fp)
	and		a1, t0, s1			#a1: bit

	lw		a2, 52($fp)			#a2: file descriptor

	jal		push_bit_to_buffer

	bne		zero, v0, error_write_buffer	#si v0 != 0, error de lectura
	srl		s1, s1, 1						#index = index >> 1

	subu	s0, s0, 1		#i--
	b		COND
END:
	li		v0, RES_OK			#Seteo v0 en el codigo de retorno 0 (RES_OK)
	b		return_write_buffer
error_write_buffer:
	li		 v0, ERROR_ESCRITURA #Seteo v0 en el codigo de retorno 1 (error de escritura)
return_write_buffer:
	lw		s0, 16($fp)		#Restauro s0
	lw		s1, 20($fp)		#Restauro s1

	lw		$fp, 28(sp)		#Recupero $fp
	lw		ra, 24(sp)		#Recupero ra

	addiu	sp, sp, 40 		#Restauro el stack
	j	ra
	.end	write_buffer




	.global push_padding
	.ent push_padding
push_padding:
	.frame	 $fp, 16,  ra
	.set	noreorder
	.cpload	t9
	.set	reorder
	subu	sp, sp, 16
	.cprestore 12
	sw		 $fp, 4(sp)		#Guardo el $fp en el frame
	move	 $fp,  sp			#Seteo el $fp

	li		 v0, SYS_write		# ver dentro de <sys/syscall.h>.
	la		 a1, PADDING       	# a1: Almaceno en a1 el caracter el padding '='.
	li		 a2, 1	      		# a2: bytes a escribir: 1b.

	syscall

	bne 	 zero, a3, error_push	#Si a3 != 0, error de escritura
	li		 t0, 1
	bne 	 v0, t0, error_push		#Si v0 != 1, error de escritura

	li		 v0, RES_OK				#Seteo el valor de retorno
	b 	 	return_push_padding
error_push:
	li		 v0, ERROR_ESCRITURA			#Seteo v0 en el codigo de retorno 1 (error de escritura)
return_push_padding:
	lw		 $fp, 4(sp)		#Recupero $fp
	addiu	sp , sp,16 		# Restauro el stack
	j	ra
	.end push_padding

	.rdata
	.align 2
PADDING:
	.asciiz	"="
CRLF:
	.asciiz	"\n"
